package com.medical.literature.gateway.filter;

import com.medical.literature.common.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

/**
 * JWT认证过滤器
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    // 不需要验证token的路径
    private static final List<String> SKIP_AUTH_URLS = Arrays.asList(
            "/user-service/auth/login",
            "/user-service/auth/register",
            "/eureka",
            "/recognition-service/recognition/process"  // 识别服务的处理接口不需要认证
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        String path = request.getURI().getPath();
        log.info("请求路径: {}", path);

        // 检查是否需要跳过认证
        if (shouldSkipAuth(path)) {
            log.info("跳过认证: {}", path);
            return chain.filter(exchange);
        }

        // 检查是否是WebSocket连接，如果是则跳过认证
        String upgradeHeader = request.getHeaders().getFirst("Upgrade");
        if ("websocket".equalsIgnoreCase(upgradeHeader)) {
            log.info("WebSocket连接，跳过认证: {}", path);
            return chain.filter(exchange);
        }

        // 获取token
        String token = getToken(request);
        if (!StringUtils.hasText(token)) {
            log.warn("未找到token，拒绝访问: {}", path);
            return unauthorized(response, "未登录或登录已过期");
        }

        // 验证token
        try {
            if (!JwtUtils.validateToken(token)) {
                log.warn("token验证失败: {}", token);
                return unauthorized(response, "token验证失败");
            }

            // 获取用户信息并添加到请求头
            Long userId = JwtUtils.getUserIdFromToken(token);
            String username = JwtUtils.getUsernameFromToken(token);
            
            log.info("token验证成功，用户ID: {}, 用户名: {}", userId, username);
            
            ServerHttpRequest mutatedRequest = request.mutate()
                    .header("X-User-Id", String.valueOf(userId))
                    .header("X-Username", username)
                    .build();

            ServerWebExchange mutatedExchange = exchange.mutate()
                    .request(mutatedRequest)
                    .build();

            return chain.filter(mutatedExchange);
        } catch (Exception e) {
            log.error("token解析异常: {}", e.getMessage());
            return unauthorized(response, "token无效");
        }
    }

    /**
     * 检查是否需要跳过认证
     */
    private boolean shouldSkipAuth(String path) {
        return SKIP_AUTH_URLS.stream().anyMatch(path::startsWith);
    }

    /**
     * 从请求中获取token
     */
    private String getToken(ServerHttpRequest request) {
        String authHeader = request.getHeaders().getFirst("Authorization");
        if (StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        return null;
    }

    /**
     * 返回未授权响应
     */
    private Mono<Void> unauthorized(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        
        String body = String.format("{\"code\":401,\"message\":\"%s\",\"data\":null}", message);
        DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
        
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100;
    }
}